package com.wanhe.soclient;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Keep;
import android.text.TextUtils;

import com.wanhe.protocollibrary.ProtocolConst;
import com.wanhe.soclient.util.NumberBytes;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * PAD本地客户端
 * Created by cdj on 2017/3/15.
 */

public class SocketClient {

    @Keep
    public static final int ERROR_STATE_CONNECTING = 2;

    @Keep
    public static final int ERROR_STATE_INIT = 1;

    @Keep
    public static final int ERROR_STATE_CLOSE = 0;

    private static final int RECEIVE_MSG = 5;
    private static final String RECEIVE_MSG_KEY = "msg_receive";
    private static final String RECEIVE_MSG_TYPE_KEY = "msg_receive_type";
    private static final int ERROR_MSG_RECEIVE = 3;
    private static final int ERROR_MSG_SEND = 4;
    private static final String ERROR_MSG_KEY = "msg_err";
    private final String IP;

    private Socket mSocket;
    private ExecutorService socketThread;
    private boolean isConnected;

    private InputStream is;
    private OutputStream os;

    @Keep
    public SocketClient(String ip) {
        this.IP = ip;
        this.socketThread = Executors.newSingleThreadExecutor();
        this.mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case ERROR_STATE_CONNECTING:
                        if (stateListener != null) {
                            stateListener.error(msg.what);
                        }
                        break;
                    case ERROR_MSG_RECEIVE:
                        if (msgChangeListener != null) {
                            msgChangeListener.error(msg.getData().getString(ERROR_MSG_KEY));
                        }
                        break;
                    case RECEIVE_MSG:
                        if (msgChangeListener != null) {
                            msgChangeListener.receiveServerMsg(msg.getData().getInt(RECEIVE_MSG_TYPE_KEY),
                                    msg.getData().getString(RECEIVE_MSG_KEY));
                        }
                }
                super.handleMessage(msg);
            }
        };
    }

    /**
     * 启动socket客户端
     *
     * @author 陈德基
     * @time 2017/3/15 15:48
     */
    @Keep
    public void startClient() {
        start();
    }

    private void startSocket() {
        try {
            this.mSocket = new Socket(this.IP, ProtocolConst.SOCKET_SERVER_PORT);
            this.isConnected = true;
            this.is = this.mSocket.getInputStream();
            this.os = this.mSocket.getOutputStream();
            if (stateListener != null) {
                stateListener.online(this);
            }
        } catch (IOException e) {
            mHandler.sendEmptyMessage(ERROR_STATE_CONNECTING);
        }
    }

    private void start() {
        socketThread.execute(new SocketRunable());
    }

    // TODO: 2017/3/15 心跳包发送功能

    private class SocketRunable implements Runnable {

        @Override
        public void run() {
            startSocket();
            while (isConnected) {
                byte[] totolLength = new byte[4];
                try {
                    if (is.read(totolLength) != -1) {
                        //获取长度
                        int length = NumberBytes.bytesToInt(totolLength);

                        //获取类型常量
                        byte[] type = new byte[4];
                        is.read(type);
                        int iType = NumberBytes.bytesToInt(type);
                        //获取包体
                        byte[] body = new byte[length - totolLength.length - type.length];
                        is.read(body);
                        String clientMsg = new String(body, "UTF-8");

                        Message message = new Message();
                        message.what = RECEIVE_MSG;
                        Bundle bundle = new Bundle();
                        bundle.putInt(RECEIVE_MSG_TYPE_KEY, iType);
                        bundle.putString(RECEIVE_MSG_KEY, clientMsg);
                        message.setData(bundle);
                        mHandler.sendMessage(message);
                    }
                } catch (IOException e) {
                    Message msg = new Message();
                    msg.what = ERROR_MSG_RECEIVE;
                    Bundle bundle = new Bundle();
                    bundle.putString(ERROR_MSG_KEY, e.getMessage());
                    msg.setData(bundle);
                    mHandler.sendMessage(msg);
                }
            }
        }
    }

    /**
     * 将带有操作类型和信息的内容转化成字节流发送给客户端
     *
     * @param type {@link com.wanhe.protocollibrary.ProtocolConst}
     * @author 陈德基
     * @time 2017/3/15 9:23
     */
    @Keep
    public void sendMsg(int type, String msg) {
        try {
            if (mSocket == null) throw new ClientNotStartedException();
            // TODO: 2017/3/14 将对象转换成字符串，再转化成字节数组，并在字节数组前面四位数字节中写入type类型
            if (TextUtils.isEmpty(msg)) {
                msg = "";
            }
            byte[] bytes = msg.getBytes("UTF-8");
            // TODO: 2017/3/14 前四位写入长度
            byte[] typeByte = NumberBytes.intToBytes(type);
            //写入协议类型常量
            byte[] resultBytes = new byte[4 + typeByte.length + bytes.length];
            byte[] length = NumberBytes.intToBytes(resultBytes.length);
            System.arraycopy(length, 0, resultBytes, 0, length.length);
            System.arraycopy(typeByte, 0, resultBytes, length.length, typeByte.length);
            System.arraycopy(bytes, 0, resultBytes, length.length + typeByte.length, bytes.length);

            //找到现有连接的所有socket客户端，然后发送字节数据

            send2Server(resultBytes);
        } catch (IOException e) {
            if (msgChangeListener != null) {
                msgChangeListener.error("发送信息失败：" + e.getMessage());
            }
        } catch (ClientNotStartedException e) {
            if (stateListener != null) {
                stateListener.error(ERROR_STATE_INIT);
            }
        }
    }

    /**
     * 发送字节数据给所有连接的客户端
     *
     * @author 陈德基
     * @time 2017/3/15 9:33
     */
    private void send2Server(byte[] msg) throws IOException {
        this.os.write(msg);
        this.os.flush();
    }

    @Keep
    public void offLine() {
        try {
            if (stateListener != null) {
                stateListener.offLine(this);
            }

            sendMsg(ProtocolConst.CLIENT_CLOSE, null);
            this.is.close();
            this.os.close();
            this.mSocket.close();

            this.isConnected = false;
            if (socketThread != null) {
                socketThread.shutdownNow();
                socketThread = null;
            }
        } catch (IOException e) {
            if (stateListener != null){
                stateListener.error(ERROR_STATE_CLOSE);
            }
        }
    }

    private Handler mHandler;

    @Keep
    public interface OnMsgChangeListener {
        void receiveServerMsg(int type, String msg);

        void error(String error);
    }

    private OnMsgChangeListener msgChangeListener;

    @Keep
    public void setMsgChangeListener(OnMsgChangeListener msgChangeListener) {
        this.msgChangeListener = msgChangeListener;
    }

    @Keep
    public interface OnClientStateListener {
        void online(SocketClient socketClient);

        void offLine(SocketClient socketClient);

        void error(int errorState);
    }

    private OnClientStateListener stateListener;

    @Keep
    public void addClientStateListener(OnClientStateListener stateListener) {
        this.stateListener = stateListener;
    }


}
